package com.leyou.auth.service;

import com.leyou.auth.config.JwtProperties;
import com.leyou.common.auth.pojo.Payload;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author 虎哥
 */
@Slf4j
@Service
public class AuthService {

    @Autowired
    private JwtProperties prop;

    @Autowired
    private UserClient userClient;

    public void login(String username, String password, HttpServletResponse response) {
        try {
            // 	1.根据用户名密码查询用户（远程调用，ly-user）
            UserDTO user = userClient.queryUserByUsernameAndPassword(username, password);
            if(user == null){
                // 用户名或密码错误
                throw new RuntimeException("用户名或密码错误！");
            }

            //	2.如果查询成功，利用私钥生成JWT
            UserInfo userInfo = new UserInfo(user.getId(), user.getUsername(), "Guest");
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, prop.getPrivateKey(), prop.getUser().getExpire());

            //	3.把JWT写入用户的cookie中
            writeCookie(response, token);
        } catch (RuntimeException e) {
            log.error("用户登录失败，原因：{}", e);
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
    }

    public UserInfo verify(HttpServletRequest request, HttpServletResponse response) {
        // 1.获取cookie
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        // 2.解析token,取出用户
        Payload<UserInfo> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserInfo.class);
        } catch (Exception e) {
            // 解析token异常，说明登录失效，或者未登录
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
        // 3.判断黑名单
        Boolean exists = redisTemplate.hasKey(payload.getId());
        if(exists != null && exists){
            // 存在，被加入了黑名单，token无效
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }

        // 4.刷新token
        // 4.1.获取token的到期时间
        Date expiration = payload.getExpiration();
        // 4.2.计算剩余时间
        long remainTime = expiration.getTime() - System.currentTimeMillis();
        // 4.3.判断剩余时间，是否小于刷新阈值10分钟
        if(remainTime <= prop.getUser().getRefreshTimeThreshold()){
            // 4.4.生成新的token
            token = JwtUtils.generateTokenExpireInMinutes(payload.getUserInfo(), prop.getPrivateKey(), prop.getUser().getExpire());
            // 4.5.写入cookie
            writeCookie(response, token);
        }
        // 5.返回用户信息
        return payload.getUserInfo();
    }

    private void writeCookie(HttpServletResponse response, String token) {
        CookieUtils.newCookieBuilder().response(response)
                .name(prop.getUser().getCookieName()).value(token)
                .domain(prop.getUser().getDomainName())
                .httpOnly(true)
                .build();
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    public void logout(HttpServletRequest request, HttpServletResponse response) {
        // 1.获取cookie
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        // 2.解析token，获取有效期，token的id
        Payload<Object> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey());
        } catch (Exception e) {
            // 解析token异常，说明登录失效，或者未登录，不做处理
            return;
        }
        // 3.把id存入redis，有效期为token的剩余有效期
        // 3.1.获取token的剩余有效期
        long remainTime = payload.getExpiration().getTime() - System.currentTimeMillis();
        // 3.2.存入Redis
        redisTemplate.opsForValue().set(payload.getId(), "1", remainTime, TimeUnit.MILLISECONDS);

        // 4.删除cookie（重写cookie，有效期为0）
        CookieUtils.deleteCookie(
                prop.getUser().getCookieName(), prop.getUser().getDomainName(), response);
    }
}
